using System;
using System.Collections.Generic;
using System.Text;
using Barrage;
using Barrage.Global;

namespace Barrage.Meshes
{
    /// <summary>
    /// Implements a singleton mesh cache with autoload functions
    /// </summary>
    public class MeshManager:IDisposable
    {
        private static MeshManager instance = null;

        /// <summary>
        /// Returns the class instance.
        /// </summary>
        public static MeshManager Meshes { get { if (instance == null) instance = new MeshManager(); return instance; } }

        private MeshManager() { createDefaultMesh();
            Engine.RegisterDisposeHandler(new EngineDisposeHandler(Dispose));
        }
        private Dictionary<string, Barrage.Mesh> meshes = new Dictionary<string, Barrage.Mesh>();

        private Barrage.Mesh defaultCube;
        /// <summary>
        /// Returns the default mesh (a cube) that's loaded when loaded shaders return error
        /// </summary>
        public static Barrage.Mesh DefaultCube { get { return instance.defaultCube; } }

        /// <summary>
        /// Creates the default cube mesh
        /// </summary>
        protected void createDefaultMesh()
        {
            defaultCube = new Cube();
        }

        /// <summary>
        /// Tells if a specified mesh exists in the mesh dictionary
        /// </summary>
        /// <param name="name">mesh name</param>
        /// <returns>True if the item exists, false otherwise</returns>
        public bool Exists(string name)
        {
            return meshes.ContainsKey(name);
        }

        /// <summary>
        /// Tries to load the mesh with the specified name (in the ModelPath folder)
        /// </summary>
        /// <param name="name">mesh name</param>
        /// <returns>If everything went well, the loaded mesh. Else, the default cube mesh</returns>
        public Mesh this[string name]
        {
            get
            {
                Mesh m = null;
                if (meshes.TryGetValue(name, out m))
                    return m;
                NLog.LogManager.GetLogger("Barrage::MeshManager").Debug("Mesh " + name + " not found. Trying to load");
                try
                {
                    m = new XMesh(Store.PathConfig[Store.MODEL_PATH] + name);
                    NLog.LogManager.GetLogger("Barrage::MeshManager").Debug("Mesh " + name + " finished loading.");
                }
                catch
                {
                    NLog.LogManager.GetLogger("Barrage::TextureManager").Debug("Error loading mesh " + name);
                    m = DefaultCube;
                }
                meshes.Add(name, m);
                return m;
            }
            set
            {
                Mesh m;
                if (meshes.TryGetValue(name, out m))
                    throw new Exception("Mesh already loaded");
                meshes.Add(name, value);
            }
        }

        /// <summary>
        /// Clear resources
        /// </summary>
        public void Dispose()
        {
            foreach (Mesh m in meshes.Values)
                if (m != defaultCube)
                    m.Dispose();
            meshes.Clear();
            defaultCube.Dispose();
            defaultCube = null;
            NLog.LogManager.GetLogger(ToString()).Debug("Disposing");
            GC.SuppressFinalize(this);
        }

        /// <summary>
        /// Destructor.Clear resources
        /// </summary>
        ~MeshManager()
        {
            Dispose();
        }
    }
}